function FuncInterface() {}
FuncInterface.isFlashReady = false;
FuncInterface.FlashLink = '';
FuncInterface.PrimarySocket = '';
FuncInterface.SPSocket = null;
FuncInterface.firstCommand = '';
FuncInterface.ReconnectTimer = ''FuncInterface.lastCt = '';
FuncInterface.IsBrowserIE = false;
FuncInterface.isReceive = true;
FuncInterface.failCount = 0;
FuncInterface.PLCount = 0;
FuncInterface.ManualClose = false;
FuncInterface.SerialNumber = 1;
FuncInterface.ChangeRelay = false;
FuncInterface.TargetUrl = '';
FuncInterface.RequestTimer = '';
FuncInterface.heartBeatTimer = '';
FuncInterface.heartBeatCount = 0;
FuncInterface.cookieFvPk = [];
FuncInterface.cookieFvDate = [];
FuncInterface.isCheckSN = false;
FuncInterface.synKey = 0;
FuncInterface.relayTime = '';
FuncInterface.localTime = {
    'last': 0,
    'now': 0
};
FuncInterface.WebSocketTest = function() {
    if (("ActiveXObject" in window)) {
        FuncInterface.IsBrowserIE = true;
    }
    if (('WebSocket' in window) == true) {
        return true;
    } else {
        var flashText = '<object id="fsSocketIE" type="application/x-shockwave-flash" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' + 'codebase="http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="800" height="300" ' + 'data="' + location.protocol + '//' + location.host + '/sport/SPSocket.swf" wmode="transparent">' + '<param name="wmode" value="transparent" />' + '<param name="movie" value="SPSocket.swf" /><param name="quality" value="high" /><param name="allowScriptAccess" value="always" />' + '<embed id="fsSocket" src="' + location.protocol + '//' + location.host + '/sport/SPSocket.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" ' + 'width="800" height="300">' + '</object>';
        $('body').append(flashText);
        return false;
    }
}
FuncInterface.BindFlash = function(device, date, gid, sessionID) {
    try {
        if (FuncInterface.IsBrowserIE == true || navigator.userAgent.indexOf("Firefox") >= 0) {
            $('#fsSocketIE')[0].isReady();
        } else {
            $('#fsSocket')[0].isReady();
        }
    } catch(ex) {} finally {
        if (FuncInterface.isFlashReady == true) {
            FuncInterface.SocketConnect(device, date, gid, sessionID);
        } else {
            setTimeout(function() {
                FuncInterface.BindFlash(device, date, gid, sessionID);
            },
            200);
        }
    }
}
FuncInterface.FlashReady = function() {
    $('#fsSocketIE').offset({
        top: -1000,
        left: -600
    });
    $('#fsSocket').offset({
        top: -1000,
        left: -600
    });
    FuncInterface.isFlashReady = true;
}
FuncInterface.SocketConnect = function(device, date, gid, sessionID) {
    if (('WebSocket' in window) == true) {
        FuncInterface.SPSocket = new SPSocket({
            urlArray: webSocketUrl,
            ssl: true,
            timeout: 5000,
            failLimit: 3,
            onConnect: function(url, targetSocket) {
                FuncInterface.TargetUrl = url;
                FuncInterface.PrimarySocket = targetSocket;
                FuncInterface.onOpen(device, date, gid, sessionID);
                window.clearTimeout(FuncInterface.heartBeatTimer);
                FuncInterface.SocketHeartBeat();
            },
            onReceive: function(event) {
                var data = '';
                try {
                    data = event.data;
                } catch(exception) {
                    RecordLog('FuncInterface.socket.onmessage()', 'sessionID: ' + sessionID + ',data receive failure\r\nexception: ' + exception.data);
                } finally {
                    FuncInterface.onMessage(data, device, date, gid, sessionID);
                }
            },
            onError: function() {
                FuncInterface.onError(sessionID, 'socket error event occurred');
            },
            onClose: function(event) {
                FuncInterface.onClose(event.code, sessionID);
            }
        });
        FuncInterface.SPSocket.SetTestSpeed('?device=' + device + '&token=' + sessionID + '/');
    } else {
        if (FuncInterface.IsBrowserIE == true || navigator.userAgent.indexOf("Firefox") >= 0) {
            FuncInterface.PrimarySocket = $('#fsSocketIE')[0];
        } else {
            FuncInterface.PrimarySocket = $('#fsSocket')[0];
        }
        if (FuncInterface.ChangeRelay == true) {
            var urlArray = [FuncInterface.TargetUrl];
            FuncInterface.PrimarySocket.connect(urlArray, device, date, gid, sessionID);
        } else {
            FuncInterface.PrimarySocket.connect(webSocketUrl, device, date, gid, sessionID);
        }
    }
}
FuncInterface.onOpen = function(device, date, gid, sessionID) {
    if (FuncInterface.firstCommand != '') {
        window.clearTimeout(FuncInterface.ReconnectTimer);
        FuncInterface.isReceive = true;
        FuncInterface.failCount = 0;
        FuncInterface.PLCount = 0;
        FuncInterface.ManualClose = false;
        FuncInterface.heartBeatCount = 0;
        FuncInterface.isCheckSN = false;
        var reconnectCommand = '';
        reconnectCommand = FuncInterface.lastCt;
        reconnectCommand['language'] = FuncInterface.firstCommand['language'];
        reconnectCommand['system'] = FuncInterface.firstCommand['system'];
        reconnectCommand['token'] = FuncInterface.firstCommand['token'];
        reconnectCommand['reconnect'] = 1;
        if (FuncInterface.firstCommand['system'].indexOf('main') >= 0 || FuncInterface.firstCommand['system'].indexOf('mobile') >= 0) {
            var array = {};
            for (var i = 0; i < FuncFavorite.ScheduleFVGroup['pk'].length; i++) {
                array[FuncFavorite.ScheduleFVGroup['date'][i]] = array[FuncFavorite.ScheduleFVGroup['date'][i]] ? array[FuncFavorite.ScheduleFVGroup['date'][i]] : [];
                array[FuncFavorite.ScheduleFVGroup['date'][i]].push(FuncFavorite.ScheduleFVGroup['pk'][i]);
            }
            reconnectCommand['fvPK'] = array;
        }
        FuncInterface.ChangeRelay == false;
        FuncInterface.SendCommand(reconnectCommand);
    } else {
        var fvPK = GetCookie('fvPK').length > 0 ? GetCookie('fvPK').split(',') : [];
        var fvDate = GetCookie('fvDate').length > 0 ? GetCookie('fvDate').split(',') : [];
        var array = {};
        for (var i = 0; i < fvPK.length; i++) {
            array[fvDate[i]] = array[fvDate[i]] ? array[fvDate[i]] : [];
            array[fvDate[i]].push(fvPK[i]);
        }
        FuncInterface.firstCommand = {
            'action': 'first',
            'system': device,
            'token': sessionID,
            'language': langFont['Font_Langue'],
            'fvPK': array
        };
        if (ballType) {
            FuncInterface.firstCommand['ball'] = ballType;
        }
        if (gid != '') {
            FuncInterface.firstCommand['type_gid'] = gid;
            FuncInterface.firstCommand['date'] = date
        } else {
            FuncInterface.firstCommand['order'] = 1;
        }
        FuncInterface.SendCommand(FuncInterface.firstCommand);
    }
}
FuncInterface.onMessage = function(data, device, date, gid, sessionID) {
    window.clearTimeout(FuncInterface.RequestTimer);
    FuncInterface.isReceive = true;
    var receiveData = pako.inflate(data, {
        to: 'string'
    });
    receiveData = FuncInterface.RemoveMark(receiveData, sessionID);
    switch (receiveData['action']) {
    case 'first':
        FuncInterface.failCount = 0;
        if (receiveData['syn'] == FuncInterface.synKey) {
            ActionFirst(receiveData);
        }
        if (device.indexOf('main') >= 0 || device.indexOf('mobile') >= 0) {
            FuncInterface.lastCt = {
                'action': 'ct',
                'type': receiveData['type'],
                'date': nowDate['string'].replaceAll('/', '')
            };
            FuncInterface.PLCount = receiveData['PLCount'] ? receiveData['PLCount'] : 0;
        } else if (device == 'Web_detail') {
            FuncInterface.lastCt = {
                'action': 'ct',
                'type': receiveData['type'],
                'date': date,
                'type_gid': gid
            };
        }
        FuncInterface.relayTime = receiveData['date'].replaceAll('-', '/');
        FuncInterface.localTime['now'] = new Date().getTime();
        FuncInterface.localTime['last'] = FuncInterface.localTime['now'];
        break;
    case 'ct':
        FuncInterface.failCount = 0;
        if (FuncInterface.lastCt['reconnect']) {
            FuncInterface.relayTime = receiveData['date'].replaceAll('-', '/');
            FuncInterface.localTime['now'] = new Date().getTime();
            FuncInterface.localTime['last'] = FuncInterface.localTime['now'];
            if (FuncInterface.lastCt['system'].indexOf('main') >= 0 || FuncInterface.lastCt['system'].indexOf('mobile') >= 0) {
                FuncInterface.lastCt = {
                    'action': 'ct',
                    'type': receiveData['type'],
                    'date': gameDate['string'].replaceAll('/', '')
                };
            } else {
                FuncInterface.lastCt = {
                    'action': 'ct',
                    'type': receiveData['type'],
                    'date': date,
                    'type_gid': gid
                };
            }
        }
        if (device.indexOf('main') >= 0 || device.indexOf('mobile') >= 0) {
            FuncInterface.PLCount = receiveData['PLCount'] ? receiveData['PLCount'] : 0;
        }
        if (receiveData['syn'] == FuncInterface.synKey) {
            ActionChangeType(receiveData);
        }
        break;
    case 'sf_over':
        FuncInterface.failCount = 0;
        break;
    case 'rf_over':
        FuncInterface.failCount = 0;
        break;
    case 'resend':
        FuncInterface.failCount = 0;
        FuncInterface.SendCommand(recentCommand);
        break;
    case 'fail':
        FuncInterface.SendCommand(recentCommand);
        break;
    case 'add':
        if (FuncInterface.PLCount == 0) {
            ActionAddGame(receiveData);
        }
        break;
    case 'del':
        if (FuncInterface.PLCount == 0) {
            ActionDelGame(receiveData);
        }
        break;
    case 'checkSN':
        if ($('#maintBlock').is(':visible')) {
            ActionMtover();
        }
        recentSN = receiveData['sn'];
        FuncInterface.failCount = 0;
        FuncInterface.relayTime = receiveData['date'].replaceAll('-', '/');
        if ((device.indexOf('main') >= 0 || device.indexOf('mobile') >= 0) && CheckCrossDay()) {
            FuncInterface.SendCommand({
                'action': 'ct',
                'type': ballType,
                'date': nowDate['string'].replaceAll('/', '')
            });
            recentCommand = {
                'action': 'ct',
                'type': ballType,
                'date': nowDate['string'].replaceAll('/', '')
            };
        } else if (receiveData['result'] == 1) {
            FuncInterface.SendCommand(FuncInterface.lastCt);
        } else {
            UnMask();
        }
        break;
    case 'resetSN':
        ActionResetSN();
        break;
    case 'update':
        if (FuncInterface.PLCount == 0) {
            ActionUpdate(receiveData);
        }
        break;
    case 'hb_over':
        FuncInterface.failCount = 0;
        break;
    case 'connect':
        FuncInterface.ChangeRelay = true;
        FuncInterface.failCount = 0;
        FuncInterface.TargetUrl = receiveData['target'];
        FuncInterface.SocketReconnect();
        break;
    case 'mt':
        FuncInterface.failCount = 0;
        ActionMt();
        break;
    case 'mt_over':
        FuncInterface.failCount = 0;
        ActionMtover();
        break;
    default:
        if (receiveData['action'].indexOf('first') == 0) {
            FuncInterface.PLCount--;
            FuncInterface.failCount = 0;
            if (receiveData['syn'] == FuncInterface.synKey) {
                ActionFirstContinue(receiveData);
            }
        } else if (receiveData['action'].indexOf('ct') == 0) {
            FuncInterface.PLCount--;
            FuncInterface.failCount = 0;
            if (receiveData['syn'] == FuncInterface.synKey) {
                ActionChangeTypeContinue(receiveData);
            }
        }
        break;
    }
}
FuncInterface.RemoveMark = function(data, sessionID) {
    var splitData = data.split('#');
    var header = splitData[0];
    var headnum = splitData[1].split('{')[0];
    var tailer = splitData[splitData.length - 2].substr(splitData[splitData.length - 2].length - 1, 1);
    var tailnum = splitData[splitData.length - 1];
    var resultData = '{ "action": "fail" }';
    if (header == 'S' && tailer == 'E' && headnum == tailnum) {
        resultData = data.replace(header + '#' + headnum, '');
        resultData = resultData.replace(tailer + '#' + tailnum, '');
    } else {
        RecordLog('FuncInterface.RemoveMark', 'sessionID: ' + sessionID + ',receive data mark remove failure,data: ' + data);
    }
    try {
        resultData = JSON.parse(resultData);
        resultData['sn'] = parseInt(headnum, 10);
    } catch(ex) {
        RecordLog('FuncInterface.RemoveMark', 'sessionID: ' + sessionID + ',receive data JSON.parse failure,data: ' + data + ',exception: ' + ex.message);
        resultData = {
            "action": "fail"
        };
    } finally {
        return resultData;
    }
}
FuncInterface.onError = function(sessionID, errorMsg) {
    if (FuncInterface.ManualClose == false && errorMsg.indexOf('socket error event occurred') < 0) {
        RecordLog('FuncInterface.onError', 'sessionID: ' + sessionID + ',' + errorMsg);
    }
}
FuncInterface.onClose = function(code, sessionID) {
    if (FuncInterface.ManualClose == false && code != 1000) {
        var reason;
        switch (code) {
        case 1001:
            reason = 'CLOSE_GOING_AWAY: 端點“離開”（going away），服務器關閉或瀏覽器導航到其他頁面';
            break;
        case 1002:
            reason = 'CLOSE_PROTOCOL_ERROR: 端點因為協議錯誤而終止連接';
            break;
        case 1003:
            reason = 'CLOSE_UNSUPPORTED: 端點收到了錯誤的數據類型而終止連接（如僅接收文本數據的終端接收到了二進制數據）';
            break;
        case 1005:
            reason = 'CLOSE_NO_STATUS: 沒有收到預期的狀態碼';
            break;
        case 1006:
            reason = 'CLOSE_ABNORMAL: 等待接收預期狀態碼時連接非正常關閉';
            break;
        case 1007:
            reason = 'Unsupported Data: 由於收到了格式不符的數據而斷開連接（如文本消息中包含了非UTF-8 數據）';
            break;
        case 1008:
            reason = 'Policy Violation: 通用狀態碼, 用於不適用1003 和1009 狀態碼，由於收到不符合約定的數據而斷開連接';
            break;
        case 1009:
            reason = 'CLOSE_TOO_LARGE: 收到過大的數據而斷開連接';
            break;
        case 1010:
            reason = 'Missing Extension: 服務端回傳的握手協定標頭不齊全, 因此客戶端斷開連接';
            break;
        case 1011:
            reason = 'Internal Error: 服務端在完成客戶端請求時發生不可預期的錯誤, 因此服務端斷開連接';
            break;
        case 1012:
            reason = 'Service Restart: 服務端因為重啟而斷開連接';
            break;
        case 1013:
            reason = 'Try Again Later: 服務器因為臨時原因斷開連接（如服務器過載因此斷開一部分客戶端連接）';
            break;
        case 1015:
            reason = 'TLS Handshake: 連接由於無法完成TLS握手而關閉（如無法驗證服務器憑證）';
            break;
        case 9999:
            reason = 'Flash Socket Closure: Flash Socket意外斷開';
            break;
        default:
            reason = "Unknown reason: " + "code = " + code;
            break;
        }
        if (code != 1006) {
            RecordLog('FuncInterface.onClose', 'sessionID: ' + sessionID + ',socket close: ,' + reason);
        }
    }
}
FuncInterface.SendCommand = function(command) {
    if (FuncInterface.failCount <= 10) {
        if ((command['action'] == 'first' || command['action'] == 'ct') && jQuery.isEmptyObject(command['reconnect'])) {
            FuncInterface.lastCt = command;
            FuncInterface.synKey = FuncInterface.synKey + 1;
            command['syn'] = FuncInterface.synKey;
        } else if (command['syn'] && command['syn'] != FuncInterface.synKey) {
            return;
        }
        if (FuncInterface.GetSocketState() != 1) {
            window.clearTimeout(FuncInterface.RequestTimer);
            FuncInterface.failCount = 0;
            FuncInterface.SocketReconnect();
        } else {
            FuncInterface.failCount++;
            var sendStr = 'S#' + FuncInterface.SerialNumber + JSON.stringify(command) + 'E#' + FuncInterface.SerialNumber;
            FuncInterface.PrimarySocket.send(sendStr);
            FuncInterface.SerialNumber = (FuncInterface.SerialNumber == 100) ? 1 : (FuncInterface.SerialNumber + 1);
            if (command['action'].indexOf('over') < 0 || FuncInterface.PLCount > 0) {
                FuncInterface.isReceive = false;
                window.clearTimeout(FuncInterface.RequestTimer);
                FuncInterface.RequestTimer = setTimeout(function() {
                    if (FuncInterface.isReceive == false) {
                        FuncInterface.SendCommand(recentCommand);
                    }
                },
                3000);
            }
        }
    } else {
        RecordLog('FuncInterface.SendCommand()', 'sessionID: ' + FuncInterface.firstCommand['token'] + ',socket command resend over limit,' + JSON.stringify(command));
    }
}
FuncInterface.GetSocketState = function() {
    if (('WebSocket' in window) == true) {
        return FuncInterface.PrimarySocket.readyState;
    } else {
        return FuncInterface.PrimarySocket.getConnectStatus();
    }
}
FuncInterface.SocketReconnect = function() {
    window.clearTimeout(FuncInterface.heartBeatTimer);
    FuncInterface.SocketClose();
    window.clearTimeout(FuncInterface.ReconnectTimer);
    var prarm = {};
    $.each(FuncInterface.firstCommand,
    function(key) {
        prarm[key] = FuncInterface.firstCommand[key] ? FuncInterface.firstCommand[key] : '';
    });
    FuncInterface.PrimarySocket.close();
    FuncInterface.SocketConnect(prarm['system'], prarm['date'], prarm['type_gid'], prarm['token']);
    FuncInterface.ReconnectTimer = setTimeout(function() {
        if (FuncInterface.GetSocketState() != 1) {
            FuncInterface.SocketReconnect();
        }
    },
    3000);
}
FuncInterface.SocketClose = function() {
    FuncInterface.ManualClose = true;
    if (FuncInterface.PrimarySocket) {
        FuncInterface.PrimarySocket.close();
    }
    window.clearTimeout(FuncInterface.heartBeatTimer);
    window.clearTimeout(FuncInterface.RequestTimer);
}
FuncInterface.SocketHeartBeat = function() {
    FuncInterface.heartBeatTimer = setTimeout(function() {
        FuncInterface.localTime['now'] = new Date().getTime();
        if (FuncInterface.localTime['now'] - FuncInterface.localTime['last'] >= 5000) {
            FuncInterface.localTime['last'] = FuncInterface.localTime['now'];
            FuncInterface.heartBeatCount++;
        }
        if (FuncInterface.heartBeatCount == 5 || FuncInterface.isCheckSN == true) {
            if (FuncInterface.PLCount > 0) {
                FuncInterface.isCheckSN == true;
            } else {
                FuncInterface.SendCommand({
                    'action': 'checkSN',
                    'sn': recentSN
                });
                FuncInterface.heartBeatCount = 0;
                FuncInterface.isCheckSN = false;
            }
        }
        FuncInterface.SocketHeartBeat();
    },
    500);
}